<template>
    <div class="container">
        <div class="position">
            <div class="item">
                <span class="icon iconfont icon-zhibei"></span>
            </div>
            <div class="item">
                <Select v-model:value="deviceId"
                  placeholder="请选择AGV"
                  show-search 
                  allow-clear 
                  @select="_selectAgv">
                    <SelectOption v-for="item in agvs" :value="item.deviceId" >
                        {{item.deviceId}}
                    </SelectOption>
                </Select>
            </div>
            <div class="item">
                <Select 
                    placeholder="请选择站点"
                    v-model:value="locationId"
                    show-search 
                    allow-clear
                    optionFilterProp="label"
                    @select="_selectLocationAndContainer"
                    >
                    <SelectOption v-for="item in locations" :value="item.uuid" :label="item.label">
                        {{item.label}}
                    </SelectOption>
                </Select>
            </div>
            <div class="item">
                <Select 
                    placeholder="请选择托盘"
                    v-model:value="containerId"
                    show-search 
                    allow-clear
                    optionFilterProp="label"
                    @select="_selectLocationAndContainer"
                    >
                    <SelectOption v-for="item in containers" :value="item.id" :label="item.label">
                        {{item.label}}
                    </SelectOption>
                </Select>
            </div>
        </div>
        <div ref="canvas" class="canvas-preview"></div>
        <Modal v-model:visible="visible"
            :width="800"
            :title="container" @ok="visible = false" ok-text="确认" cancel-text="取消">
            <Table
                :columns="columns"
                :data-source="goods"
                bordered
                :pagination="false"
             />
        </Modal>
    </div>
</template>
<script>
    import CanvasPanel from "@/draw/plugins/canvasPanel";
    import Command from "@/draw/plugins/command";
    import G6 from "@antv/g6/lib"
    import registerShape from "@/draw/shape/index";
    import registerBehavior from "@/draw/behavior/index";
    import {unLockLocation} from "@/api/map/index"
    registerShape(G6);
    registerBehavior(G6);
    import {details} from "../../../api/map"
    import {Base64} from 'js-base64';
    import { Select , SelectOption, Modal, Table, message} from "ant-design-vue";
    import {getShapeName} from "@/draw/util/clazz";
    import { generateUUID } from "three/src/math/MathUtils";
import node from "@/draw/shape/node";

    export default{
        name: 'preview',
        data(){
            return {
                pageRef: null,
                origin: {
                    sourceNode: {},
                    targetNode: {},
                    position: {},
                    points: {},
                    currentPoint: {}
                },
                lineNodeIds: {},
                pointNodeIds: {},
                pointX: 0,
                pointY: 0,
                width : 4017,
                height: 5244,
                visible: false,
                container: "",
                goods: [],
                agvs: [],
                deviceId: null,
                locations: [],
                locationId: null,
                containers: [],
                containerId: null,
                angleFlag: {},
                columns: [
                    {
                        title: '物料名称',
                        dataIndex: 'itemName',
                    },
                    {
                        title: '物料编码',
                        dataIndex: 'itemCode',
                    },
                    {
                        title: '任务号',
                        dataIndex: 'itemWorkcode',
                    },
                    {
                        title: '物料数量',
                        dataIndex: 'itemQty',
                    },
                    {
                        title: '单位',
                        dataIndex: 'itemUnit',
                    },
                ]
            }
        },
        watch: {
        },
        components: {
            Select,
            SelectOption,
            Modal,
            Table
        },
        mounted(){
            this.pageRef = this.$refs.canvas;
            this._init();
            this._details(this.$route.query);
        },
        methods: {

            _init(){
                // x轴偏移量
                this.pointX = 246.106934/0.08 - (this.width/2);
                // y轴偏移量
                this.pointY = (this.height/2)-(53.840420/0.08);
            },

            /**
             * 选中agv
             */
            _selectAgv(value){
                const item = this.agvs.find(item => item.deviceId == value);
                this.graph.zoomTo(4);
                this.handleGotoNode(item.id);
            },

            /**
             * 选择站点or托盘
             * @param value 
             */
            _selectLocationAndContainer(value){
                this.graph.zoomTo(10);
                this.handleGotoNode(value);
            },

            /**
             * 
             * @param id 
             */
            handleGotoNode(id){
                // 找到视口中心
                const viewCenter = {
                    x: this.graph?.get('width') / 2,
                    y: this.graph?.get('height') / 2
                };
                const node = this.graph?.findById(id);
                const modelCenter = this.graph?.getPointByCanvas(viewCenter.x, viewCenter.y);
                if (node && modelCenter) {
                    const { x, y, width, height } = node.getBBox();
                    const scale = this.graph?.getZoom() || 1
                    // 画布平移的目标位置，最终目标是graph.translate(dx, dy);
                    const dx = modelCenter.x - x;
                    const dy = modelCenter.y - y;
                    this.graph?.translate(((dx - width / 2) * scale), (dy - height / 2) * scale);
                }
            },

            //查找详细
            _details(query){
                this.id = JSON.parse(Base64.decode(Object.keys(query).map(item => query[item]).join(""))).id;
                details(this.id).then(res => {
                    const data = res.data;
                    this.map = {id: this.id, name: data.name, type: data.type, remark: data.remark};
                    const model = {id: '1',size:[this.width, this.height], clazz: 'map', type: 'map-node', x: -this.width/2, y: -this.height/2, src: data.path};
                    this.nodes = [model, ...data.nodes?.map(item => {
                        const size = item.size?.split(",");
                        return {
                            ...item, 
                            visible: item.clazz === 'car' ? false: true,
                            color: item.clazz === 'location' && item.locationLock == '1' ? '#FFE7BA': item.color,
                            backColor: item.clazz === 'location' && item.locationLock === '1' ? '#CDBA96': '#fff',
                            id: item.uuid, 
                            size: size && [parseInt(size[0]), parseInt(size[1])]
                        }
                    })];
                    this.locations = this.nodes.filter(item => item.clazz === 'location');
                    this.getLocationAndGoods();
                    this.componentDidMount();
                    this.listerMessage();
                });
            },

            /**
             * 获取站点和站点下的货物
             */
            getLocationAndGoods(){
                const userId = localStorage.getItem("userId");
                this.$store.state.socket.webSockt.send(JSON.stringify({'eventName': '__getContainer', data: {
                    'userId': userId
                }}));
            },
        
            /**
             * 监听socket消息
             */
            listerMessage(){
                this.$store.state.socket.webSockt.onmessage = (res) => {
                    const result = JSON.parse(res.data);
                    switch(result.eventName){
                        case '__container': this.updateLocation(result.data); break;
                        case '__heart': this.updateCatPosition(result.data); break;
                        default: break;
                    }
                }
            },

            /**
             * 更新站点下的托盘
             */
            updateLocation(data){
                // 添加托盘
                data.forEach(item => {
                    const id = generateUUID();
                    this.graph.executeCommand('add', {
                        type: 'node',
                        addModel: {
                            type: getShapeName('container'),
                            clazz: 'container',
                            x: item.x,
                            y: item.y,
                            size: [18,9],
                            id: id,
                            label: item.label,
                            goods: item.goods,
                            angle: !item.horizontal ? 90 : false
                        }
                    });
                    this.containers.push({id: id, label: item.label});
                })
            },

            /**
             * 更新小车位置
             */
            updateCatPosition(data){
                // 抽取agv ip
                const deviceIds = data.map(item => item.ip);
                // 查找在线的agv
                const nodes = this.graph.cfg.data.nodes.filter(item => item.deviceId && deviceIds.includes(item.deviceId));
                // agv
                this.agvs = [];
                nodes.forEach(element => {
                    const item = data.find(item => item.ip === element.deviceId);
                    if(item.connectState === '1'){
                        // agv 当前x轴位置
                        let currentX= parseInt((item.x/0.08) + this.pointX);
                        // agv 当前y轴位置
                        let currentY = parseInt(this.pointY-(item.y/0.08));
                        // agv
                        this.agvs.push(element)
                        if(this.origin.position?.[element.id]){
                            // 上一次x轴位置
                            const positionX = this.origin.position[element.id].x;
                            // 上一层y轴位置
                            const positionY = this.origin.position[element.id].y;
                            if(positionX != currentX || positionY != currentY){
                                // 当前位置
                                // this.origin.position = {...this.origin.position, [element.id]: {x: currentX, y: currentY}};
                                // this.checkTravel(element.id, currentX, currentY);
                                // // 执行agv动画
                                this.runCarAnimate(element.id, currentX, currentY);
                                // // 画点
                                // this.drawPoint(element.id);
                                // // 删除线
                                // this.removeLine(element.id)
                                // // 画线
                                // this.addLine(element.id);
                            }
                        }else{
                            let nextX = null;
                            let nextY = null;
                            if(item.path){
                                // let index = localStorage.getItem("origin_index_"+element.id);
                                // if(!index){
                                //     localStorage.setItem("origin_index_"+element.id, 0);
                                // }
                                // // 取线索引值
                                // this.origin[element.id] = {index: index ? parseInt(index) : 0};
                                // // 把索引设置到缓存防止丢失
                                // // localStorage.setItem("origin_index_"+element.id, 0);
                                // let paths = item.path.split("|");
                                // // 当前点
                                // this.origin.currentPoint = {
                                //     ...this.origin.currentPoint,
                                //     [element.id]: [currentX, currentY]
                                // }
                                // this.origin.points = {...this.origin.points,[element.id]: [...paths.map(item => 
                                //         item.split(",").map((e, index) => index === 0 ? parseInt((e/0.08) + this.pointX) : parseInt(this.pointY-(e/0.08))))]}
                                // /// ***>> start 计算agv的行驶方向 <<********
                                // const nextPoint = this.origin.points[element.id] ? this.origin.points[element.id][this.origin[element.id].index] : null;
                                // console.log("下个路线点", nextPoint)
                                // // const nextX = this.origin.points[element.id][this.origin[element.id].index+1][0];
                                // // const nextY = this.origin.points[element.id][this.origin[element.id].index+1][1];
                                // nextX = nextPoint[0];
                                // nextY = nextPoint[1];
                                // /// ***>> end 计算agv的行驶方向 <<********
                                // // this.checkTravel(element.id, currentX, currentY);
                                // // 画点
                                // this.drawPoint(element.id);
                                // // 删除线
                                // this.removeLine(element.id)
                                // // 画线
                                // this.addLine(element.id);
                            }
                            const updateModel = this.graph.findById(element.id);
                            const model = updateModel.getModel();
                            const angle = this.calculateAngle({x: currentX, y:currentY}, {x: nextX, y: nextY})
                            this.graph.updateItem(updateModel, {
                                x: currentX,
                                y: currentY,
                                size: nextX ? currentX > nextX ? [model.size[0], model.size[1]] :
                                    [-model.size[0],  model.size[1]] : [model.size[0], model.size[1]],
                                angle: nextX ? currentX > nextX ? angle+180 : angle : null,
                                visible: true,
                            })
                            // 当前位置
                            this.origin.position = {...this.origin.position, [element.id]: {x: currentX, y: currentY}};
                        }
                    }
                });    
            },

            /**
             * 检查行驶方向
             */
            checkTravel(key, currentX, currentY){
                // 车辆行驶方向
                const nextPoint = this.origin.points[key][this.origin[key].index];
                const nextX = nextPoint[0];
                const nextY = nextPoint[1];
                let flag = false;
                const model = this.graph.findById(key).getModel();
                // agv向左行驶并大于下一个点
                if(model.x > currentX && model.y === currentY && currentX <= nextX){ 
                    flag = true;
                }else if(model.x < currentX && model.y === currentY && currentX >= nextX){ // 向右判断
                    flag = true;
                }else if(model.x > currentX && model.y > currentY && currentX <= nextX && currentY <= nextY){ // 向左上判断
                    flag = true;
                }else if(model.x < currentX && model.y > currentY && currentX >= nextX && currentY <= nextY){ // 向右上判断
                    flag = true;
                }else if(model.x === currentX && model.y > currentY && currentY <= nextY){ //向上判断
                    flag = true;
                }else if(model.x === currentX && model.y < currentY && currentY >= nextY){ // 向下判断
                    flag = true;
                }else if(model.x > currentX && model.y < currentY && currentX <= nextX && currentY >= nextY){// 左下判断
                    flag = true;
                }else if(model.x < currentX && model.y < currentY && currentX >= nextX && currentY >= nextY){ // 右下判断
                    flag = true;
                }
                // 当前点
                this.origin.currentPoint = {
                    ...this.origin.currentPoint,
                    [key]: [currentX, currentY]
                };
                if(flag){
                    let index = localStorage.getItem("origin_index_"+key);
                    this.origin[key].index = parseInt(index) + 1;
                    if(this.origin[key].index === this.origin.points[key].length - 1){
                        localStorage.setItem("origin_index_"+key, 0); 
                    }else{
                        localStorage.setItem("origin_index_"+key, parseInt(index)+1); 
                    }
                    this.angleFlag = {
                        ...this.angleFlag,
                        [key]: true
                    };
                }
            },

            /**
             * 绘制点
             * @param key 
             */
            drawPoint(key){
                // 删除点
                if(this.pointNodeIds[key]?.length > 0){
                    this.pointNodeIds[key].forEach(id => {
                        this.graph.removeItem(this.graph.findById(id));
                    })
                }
                // 点节点ID
                this.pointNodeIds[key] = [];
                const currentPoint = this.origin.currentPoint[key];
                let uuid = generateUUID();
                this.graph.executeCommand('add', {
                    type: 'node',
                    addModel: {
                        type: getShapeName('point'),
                        clazz: 'point',
                        x: currentPoint[0],
                        y: currentPoint[1],
                        id: uuid,
                        visible: false
                    }
                });
                this.pointNodeIds[key].push(uuid);
                // 绘制点
                this.origin.points[key].forEach((item, index) => {
                    let uuid = generateUUID();
                    this.graph.executeCommand('add', {
                        type: 'node',
                        addModel: {
                            type: getShapeName('point'),
                            clazz: 'point',
                            x: item[0],
                            y: item[1],
                            id: uuid,
                            visible: false
                        }
                    });
                    this.pointNodeIds[key].push(uuid);
                });
            },

            /**
             * 删除线
             * @param key 
             */
            removeLine(key){
                // 删除线
                if(this.lineNodeIds[key]?.length > 0){
                    this.lineNodeIds[key].forEach(id => {
                        this.graph.removeItem(this.graph.findById(id));
                    })
                 }
                // 线节点ID
                this.lineNodeIds[key] = [];
            },

            /**
             * 画线
             * @param key
             */
            addLine(key){
                if(this.pointNodeIds[key].length > 2){
                    let sourceId = this.pointNodeIds[key][0];
                    let targetId = this.pointNodeIds[key][this.origin[key].index+1];
                    // 画线
                    this.drawLine(sourceId, targetId, key);
                    // 做动画
                    this.runAnimate();
                }
            },

            /**
             * 计算角度
             * @param p1 
             * @param p2 
             */
            calculateAngle(p1, p2){
                var deltaX = p2.x - p1.x;
                var deltaY = p2.y - p1.y;
                // 计算arctan时需要考虑deltaY的正负来确定象限
                var angle = Math.atan2(deltaY, deltaX) * 180 / Math.PI;
                // 转换为0-360度范围的角度
                return angle < 0 ? angle + 360 : angle;
            },

            /**
             * 执行agv动画
             */
            runCarAnimate(key, x, y){
                const shape = this.graph.findById(key);
                const that = this;
                shape.getContainer().animate(()=>{
                    const model = this.graph.findById(key).getModel();
                    let xx = x < model.x ? model.x - x : x - model.x;
                    let yy = y < model.y ? model.y - y : y - model.y;
                    let updateModel = null;
                    // 8个方向计算执行不同的方法
                    if(model.x > x && model.y === y){
                        updateModel= {x: parseInt(model.x - xx*0.1)};
                    }else if(model.x < x && model.y === y){
                        updateModel= {x: parseInt(model.x + xx*0.1)};
                    }else if(model.x > x && model.y > y){
                        updateModel= {x: parseInt(model.x - xx*0.1), y: parseInt(model.y - yy*0.1)};
                    }else if(model.x < x && model.y > y){
                        updateModel= {x: parseInt(model.x + xx*0.1), y: parseInt(model.y - yy*0.1)};
                    }else if(model.x === x && model.y > y){
                        updateModel= {y: parseInt(model.y - yy*0.1)};
                    }else if(model.x === x && model.y < y){
                        updateModel= {y: parseInt(model.y + yy*0.1)};
                    }else if(model.x > x && model.y < y){
                        updateModel= {x: parseInt(model.x - xx*0.1), y: parseInt(model.y + yy*0.1)};
                    }else if(model.x < x && model.y < y){
                        updateModel= {x: parseInt(model.x + xx*0.1), y: parseInt(model.y + yy*0.1)};
                    }
                    if(updateModel){
                        this.graph.updateItem(shape, updateModel);
                    }
                    // if(!updateModel && that.angleFlag?.[key]){
                    //     console.log("更新角度")
                    //     that.angleFlag[key] = false;
                    //     const nextPoint = this.origin.points[key][this.origin[key].index];
                    //     const nextX = nextPoint[0];
                    //     const nextY = nextPoint[1];
                    //     const angle = this.calculateAngle({x: x, y: y}, {x: nextX, y: nextY});
                    //     // 更换点位重新计算角度
                    //     this.graph.updateItem(this.graph.findById(key), {
                    //         angle: angle,
                    //     });
                    // }
                }, {duration: 4000, repeat: false})
            },

            /**
             * 计算车辆的行驶方向
             * @param point1 
             * @param point2 
             */
            calculateDirection(point1, point2){
                // 计算速度向量的角度
                let oldAngle = Math.atan2(point2.y, point2.x);
                let newAngle = Math.atan2(point1.y, point1.x);
                // 计算两个角度之间的差异，即行驶方向变化
                let directionChange = newAngle - oldAngle;
                // 将角度变化转换为一个有向角的范围（-π < φ < π）
                if (directionChange > Math.PI) directionChange -= 2 * Math.PI;
                else if (directionChange < -Math.PI) directionChange += 2 * Math.PI;
                return directionChange;
            },

            /**
             * 执行动画
             */
            runAnimate(){
                const shape = this.graph.findById(this.origin.sourceNode.id + "_" + this.origin.targetNode.id);
                const startPoint = shape.getModel().startPoint;
                const endPoint = shape.getModel().endPoint;
                let x = startPoint.x;
                let y = startPoint.y;
                let xx = endPoint.x < startPoint.x ? startPoint.x - endPoint.x : endPoint.x  - startPoint.x;
                let yy = endPoint.y < startPoint.y ? startPoint.y - endPoint.y : endPoint.y - startPoint.y;
                shape.getContainer().animate(()=>{
                    x = endPoint.x < startPoint.x ? x - xx*0.01 : x + xx*0.01;
                    y = endPoint.y < startPoint.y ? y - yy*0.01 :  y + yy*0.01;
                    if(
                        (endPoint.x < startPoint.x && x >= endPoint.x)
                            ||
                            (endPoint.x > startPoint.x && x <= endPoint.x)
                        ){
                            this.graph.updateItem(shape, {endPoint: {x: x, y: y}});
                        }
                    }, {duration: 4000, repeat: false})
            },

           /**
            *  画线
            * @param sourceId 
            * @param targetId 
            */
            drawLine(sourceId, targetId, key){
                this.origin.sourceNode = this.graph.findById(sourceId).getModel();
                this.origin.targetNode = this.graph.findById(targetId).getModel();
                this.graph.executeCommand('add', {
                    type: 'node',
                    addModel: {
                        type: 'quadratic',
                        clazz: 'quadratic',
                        source: this.origin.sourceNode.id,
                        target: this.origin.targetNode.id,
                        id: this.origin.sourceNode.id + "_" + this.origin.targetNode.id,
                        label: '路线',
                        lineWidth: 1,
                        color: '#6495ED',
                        isArrow: true,
                    },
                });
                this.lineNodeIds[key].push(this.origin.sourceNode.id + "_" + this.origin.targetNode.id);
            },

            /**
             * 斜率
             * @param point1
             * @param point2
             * @returns {number}
             */
             slope(point1, point2){
                return (point2.y-point1.y)/(point2.x - point1.x);
            },

            componentDidMount() {
                this.cmdPlugin = new Command();
                const canvasPanel = new CanvasPanel({container: this.pageRef});
                let plugins = [this.cmdPlugin, canvasPanel];
                this.graph = new G6.Graph({
                    plugins: plugins,
                    container: this.pageRef,
                    sortBylevel: false,
                    modes: {
                        default: ['drag-canvas', 'clickSelected'],
                        view: [],
                        edit: ['drag-canvas', 'dragNode', 'clickSelected', 'dragPanelItemAddNode', 'itemAlign', 'dragAnchor']
                    },
                    maxZoom: 1000,
                    animate: true
                });
                const nodes = this.nodes;
                this.graph.map = this.map;
                this.graph.setMode("model");
                this.graph.data({nodes: nodes, edges: this.edges});
                this.graph.render();
                this.initEvents();
            },

            initEvents() {
            
                this.graph.on('node:click', (e) => {
                    const item = e.item;
                    const model = item?.getModel();
                    if(model?.clazz === 'container'){
                        this.visible = true;
                        this.container = "托盘编号："+model.label;
                        this.goods = model.goods;
                    }
                    if(model?.clazz === 'location' && model.locationLock === '1'){
                        const that = this;
                        Modal.confirm({
                            title: '确定要解锁站点么？',
                            okText: '确认',
                            cancelText: '取消',
                            onOk() {
                                unLockLocation(model.id).then(res => {
                                    if(res.code === 'success'){
                                        message.success("站点解锁成功");
                                        that.graph.updateItem(that.graph.findById(model.id), {
                                            color: '#6FEE79',
                                            locationLock: '0',
                                            backColor: '#fff',
                                            textColor: '#000'
                                        })
                                    }
                                })
                            }
                        })
                    }
                });

                this.graph.on('wheel', (e) => {
                    const maxZoom = graph.get('maxZoom');
                    const zoom = graph.getZoom();
                    let currentZoom = zoom
                    if(e.deltaY < 0){
                       // 向下滚动
                        currentZoom = zoom - 0.05;
                    }else{
                        // 向上滚动
                        currentZoom = zoom + 0.05;
                    }
                    if(currentZoom > maxZoom){
                        currentZoom = maxZoom;
                    }
                    this.graph.zoomTo(currentZoom);
                });
                this.graph.on('afterdelete', (e) => {
                    this.graph.set("selectedItems", []);
                    this.graph.emit('afteritemselected', e);
                })
                const graph = this.graph; 
                const zoom = graph.getZoom();
                this.graph.zoomTo(zoom + 0.5);
            },
        }
    }
</script>
<style>
@font-face {
  font-family: "iconfont"; /* Project id  */
  src: url('iconfont.ttf?t=1726804094979') format('truetype');
}

.iconfont {
  font-family: "iconfont" !important;
  font-size: 16px;
  font-style: normal;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

.container{
  width: 99%;
  height: 99%;
}
.canvas-preview{
  width: 100%;
  height: 100%;
}

.icon-zhibei:before {
  content: "\e60c";
}
.position{
    position: absolute;
    right: 20px;
    top: 20px
}
.position .iconfont{
    font-size: 30px;
    float: right;
    /* color: red; */
}
.position .item{
    width: 150px;
    height: 50px;
}
.position .ant-select{
    width: 100%;
}
.position .ant-select-selector{
    background-color: rgba(0, 0, 0 ,0) !important;
}
</style>